package com.banmao.greedysnake.gamepanel.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson2.JSON;
import com.banmao.greedysnake.gamepanel.service.Painter;
import com.banmao.greedysnake.handler.PositionKeyPressEventHandler;
import com.banmao.greedysnake.util.SpringContextUtil;
import com.banmao.greedysnake.vo.Constants;
import com.banmao.greedysnake.vo.Snake;
import de.felixroske.jfxsupport.FXMLController;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.Initializable;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.input.KeyEvent;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.paint.Color;
import javafx.scene.shape.ArcType;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;

@Slf4j
@FXMLController
public class GamePanelCcontroller implements Initializable {

    public static Double canvas_width;
    public static Double canvas_height;

    public static Timer timer;

    private static Snake snake;
    private static GraphicsContext gc;

    public Canvas canvas;
    public Text score_text; // 得分
    public Text gameStatus_text; // 游戏状态提示
    public Text speed_text; // 速度提示
    public Text title_text; // 标题
    public Text title_text2;
    public Button start_button;
    public Button pause_button;
    public Text tip1;
    public Text tip2;

    private Boolean isFail = false;
    private Boolean isStart = false;
    private Integer speed = 250; // 记录 sanke 的速度

    private MediaPlayer mediaPlayer;

    public Boolean getIsFail() {
        return isFail;
    }

    // 游戏难度与score的关系
    private HashMap<Integer, Integer> hardLevel = new HashMap<>() {
        {
            // key--score，value--刷新频率，也就是难度
            put(0, 250);
            put(3, 200);
            put(6, 150);
            put(9, 130);
            put(12, 110);
            put(15, 90);
            put(20, 70);
            put(30, 50);
            put(45, 30);
        }
    };

    public void setFail(Boolean fail) {
        isFail = fail;
    }

    private PositionKeyPressEventHandler positionKeyPressEventHandler;

    @Autowired
    public void setPositionKeyPressEventHandler(PositionKeyPressEventHandler positionKeyPressEventHandler) {
        this.positionKeyPressEventHandler = positionKeyPressEventHandler;
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {

        SpringContextUtil.registerSingleton(GamePanelCcontroller.class.getSimpleName(), this);

        Platform.runLater(() -> {
            setFont(title_text, "huyou.ttf", 0);
            setFont(title_text2, "huyou.ttf", 0);
            setFont(score_text, "happy.ttf", 0);
            setFont(speed_text, "happy.ttf", 0);
            setFont(gameStatus_text, "FZSJ-TGYBXSJW.TTF", 10);
            setFont(start_button, "whiteStyle.ttf", 0);
            setFont(pause_button, "whiteStyle.ttf", 0);
            setFont(tip1, "FZSJ-TGYBXSJW.TTF", 0);
            setFont(tip2, "FZSJ-TGYBXSJW.TTF", 0);
        });

        new Thread(() -> {
            try {
                URL resource = this.getClass().getResource("/media/HopesAndDreams.mp3");
                mediaPlayer = new MediaPlayer(new Media(resource.toURI().toString()));
                mediaPlayer.play();
            } catch (URISyntaxException e) {
                log.error("[Media加载]失败, msg:{} :", e.getMessage(), e);
            }
        }).start();

        gc = canvas.getGraphicsContext2D();
        gc.setFill(Color.WHITE);
        gc.setLineWidth(1);

        Constants.CANCAS_WIDTH = canvas.getWidth();
        Constants.CANCAS_HEIGHT = canvas.getHeight();
        log.info("生成画布，width: {}, height: {}", canvas.getWidth(), canvas.getHeight());

        // 游戏初始化
        init();

        // 监听score改变事件
        score_text.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
                Integer speed = hardLevel.get(Integer.parseInt(newValue));
                if(speed != null) {
                    timer.cancel();
                    log.info("难度变更 speed: {}", speed);
                    timer = new Timer();
                    GamePanelCcontroller.this.speed = speed;
                    speed_text.setText(String.valueOf(speed));
                    timer.scheduleAtFixedRate(new MoveTask(gc, snake), speed, speed);
                }
            }
        });

    }

    private void setFont(Text text, String fontFamily, int enlarge) {
        double title_size = text.getFont().getSize();
        Font font = Font.loadFont(this.getClass().getResourceAsStream("/fonts/" + fontFamily), title_size + enlarge);
        text.setFont(font);
    }
    private void setFont(Button button, String fontFamily, int enlarge) {
        double title_size = button.getFont().getSize();
        Font font = Font.loadFont(this.getClass().getResourceAsStream("/fonts/" + fontFamily), title_size+ enlarge);
        button.setFont(font);
    }

    private void init() {
        snake = new Snake();
        positionKeyPressEventHandler.setSnake(snake);
        Painter.draw(gc, snake);
    }

    // 移动测试
    public void moveTest(ActionEvent actionEvent) {
        snake.move();
        Painter.draw(gc, snake);
    }

    public void moveTestUp(ActionEvent actionEvent) {
        snake.setNextDirection(Snake.Direction.UP);
        snake.move();
        Painter.draw(gc, snake);
    }

    public void moveTestDown(ActionEvent actionEvent) {
        snake.setNextDirection(Snake.Direction.DOWN);
        snake.move();
        Painter.draw(gc, snake);
    }

    public void moveTestLeft(ActionEvent actionEvent) {
        snake.setNextDirection(Snake.Direction.LEFT);
        snake.move();
        Painter.draw(gc, snake);
    }

    public void moveTestRight(ActionEvent actionEvent) {
        snake.setNextDirection(Snake.Direction.RIGHT);
        snake.move();
        Painter.draw(gc, snake);

    }

    // 开始游戏
    public void startGame(ActionEvent actionEvent) {
        if (isFail) {
            // 游戏已失败，需要重新开始
            init();
            setFail(false);
        }
        if(!isStart) {
            if(mediaPlayer != null) {
                mediaPlayer.play();
            }
            isStart = true;
            timer = new Timer();
            timer.scheduleAtFixedRate(new MoveTask(gc, snake), 0, speed);

            canvas.addEventHandler(KeyEvent.KEY_PRESSED, positionKeyPressEventHandler);
            gameStatus_text.setText("游戏进行中");
        }
    }

    // 暂停游戏
    public void pauseGame(ActionEvent actionEvent) {
        if(isStart) {
            isStart = false;
            timer.cancel();

            canvas.removeEventHandler(KeyEvent.KEY_PRESSED, positionKeyPressEventHandler);
            gameStatus_text.setText("游戏已暂停");
        }
        if(isFail) {
            if(mediaPlayer != null) {
                mediaPlayer.pause();
            }
            Integer score = snake.getScore();
            // 另起线程记录
            new Thread(() -> recordHistory(score)).start();
        }
    }

    // 记录历史记录
    private static void recordHistory(Integer score) {
        Path path = Paths.get("./static/history.json");
        try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE)) {
            ByteBuffer readByteBuffer = ByteBuffer.allocate(1024);
            int len;
            StringBuilder sb = new StringBuilder();
            while ((len = fileChannel.read(readByteBuffer)) != -1) {
                readByteBuffer.flip();
                byte[] array = readByteBuffer.array();
                String s = new String(array, 0, len);
                sb.append(s);
                readByteBuffer.clear();
            }
            List<Map> histories = new ArrayList<>();
            if(sb.length() > 0) {
                histories = JSON.parseArray(sb.toString(), Map.class);
            }

            String now = DateUtil.now();
            HashMap<String, String> map = new HashMap<>() {
                {
                    put("time", now);
                    put("score", String.valueOf(score));
                }
            };
            histories.add(map);

            String json = JSON.toJSONString(histories) + "\n";
            ByteBuffer byteBuffer = ByteBuffer.allocate(json.length());
            byteBuffer.put(json.getBytes());
            byteBuffer.flip();
            fileChannel.position(0);
            fileChannel.write(byteBuffer);

        } catch (IOException e) {
            File toFile = path.toFile();
            if(!toFile.exists()) {// 可能没有文件，尝试创建
                File file = FileUtil.touch(toFile);
                if (file.exists()) {
                    recordHistory(score);
                    return;
                }
            }
            log.error("历史记录写入失败, msg:{} :", e.getMessage(), e);
        }
    }


    class MoveTask extends TimerTask {

        private Snake snake;
        private GraphicsContext gc;
        public MoveTask(GraphicsContext gc, Snake snake) {
            this.gc = gc;
            this.snake = snake;
        }

        @Override
        public void run() {
            //log.info("snake行动");
            try {
                snake.move();
                // 移动之后更改score
                Integer score = snake.getScore();
                score_text.setText(String.valueOf(score));

                Painter.draw(gc, snake);
            } catch (Exception e) {
                log.error("程序运行错误！msg:{} :", e.getMessage(), e);
            }
        }
    }

    private void drawShapes(GraphicsContext gc) { // 绘画上下文环境对象，在这个系列api中有点画笔对象那意思
        gc.setFill(Color.GREEN); // 绘画上下文填充绿色，给画笔的填充作用属性填充颜料为绿色
        gc.setStroke(Color.BLUE); // 绘画上下文设置线型和颜色,给画笔的线性绘画笔筒内填充蓝色颜料
        gc.setLineWidth(1); // 绘画上下文设置线条粗细
        gc.strokeLine(100, 10, 10, 10); // 根据对应坐标参数划线，起点坐标(x1,y1),重点坐标(x2,y2)
        gc.fillOval(10, 60, 30, 30); // 画一个填充型椭圆，参数分别是（x,y),宽w,和高h
        gc.strokeOval(60, 60, 30, 30); // 画一个线状型椭圆，参数分别是（x,y),宽w,和高h
        gc.fillRoundRect(110, 60, 30, 30, 10, 10); // 画一个圆填充型角矩形
        gc.strokeRoundRect(160, 60, 30, 30, 10, 10); // 画一个线性状圆角矩形
        gc.fillArc(10, 110, 30, 30, 45, 240, ArcType.OPEN); // 创建填充式拱形
        gc.fillArc(60, 110, 30, 30, 45, 240, ArcType.CHORD); // 创建填充式拱形
        gc.fillArc(110, 110, 30, 30, 45, 240, ArcType.ROUND); // 创建填充式原型拱形
        gc.strokeArc(10, 160, 30, 30, 45, 240, ArcType.OPEN);
        gc.strokeArc(60, 160, 30, 30, 45, 240, ArcType.CHORD);
        gc.strokeArc(110, 160, 30, 30, 45, 240, ArcType.ROUND);
        gc.fillPolygon(new double[]{10, 40, 10, 40},
                new double[]{210, 210, 240, 240}, 4); // 创建填充式多边形
        gc.strokePolygon(new double[]{60, 90, 60, 90},
                new double[]{210, 210, 240, 240}, 4); // 创建线性多边形
        gc.strokePolyline(new double[]{110, 140, 110, 140},
                new double[]{210, 210, 240, 240}, 4); // 创建多段线
    }
    
}
